#include "stdio.h"
#include <iostream>
#include <fstream>
#include <string>
#include <exception>
#include <cmath>

#include "../algebra/Matrix.h"
#include "../algebra/MatrixOps.h"
#include "../geom/GeomMatrix.h"

#include "../UnitTest++/src/UnitTest++.h"

using namespace algebra;

SUITE(MatrixTests)
{

	TEST(TECGRAFICASTEST)
	{
		float mf[9] = {1.910, -0.985, 0.058,
						-0.533, 2.000, -0.118, -0.288,-0.028, 0.896};
		algebra::Matrix3f m1(mf);
		std::cout<<"MATRIZ TECGRAFICAS:\n"<<m1;
		// RGB = m1 * XYZ
		// Necesitamos la inversa de m1 para poder hacer:
		// XYZ = Im1 * RGB
		float im1f[] = {1.078, 0.531, 0, 0.308, 0.653, 0.066, 0.356, 0.191, 0.118};
		algebra::Matrix3f Im1(im1f);
		float rf[]={1,0.0};
		float gf[]={0,1.0};
		float bf[]={0,0.1};
		algebra::Vector3f r(rf);
		algebra::Vector3f g(gf);
		algebra::Vector3f b(bf);

		algebra::Vector3f X = Im1 * r;
		algebra::Vector3f Y = Im1 * g;
		algebra::Vector3f Z = Im1 * b;

		std::cout <<"X:"<<X<<"Y:"<<Y<<"Z:"<<Z;
		float xr = X[0] / (X[0] + Y[0]+Z[0]);
		float yr = X[0] / (X[0] + Y[0]+Z[0]);
		float zr = X[0] / (X[0] + Y[0]+Z[0]);




	}

    TEST(Check_2x2_MatrixLoad)
	{
		algebra::Matrix2f m2;
		m2.at(0,0) = 1;
		m2.at(0,1) = 2;
		m2.at(1,0) = 3;
		m2.at(1,1) = 4;

		float m3a[] ={1,2,3,4};
		algebra::Matrix2f m3(m3a);
		// |1  3|
		// |2  4|

		CHECK_EQUAL(m3, m2);
    }

	TEST(Check_2x2_MatrixTransposition)
	{
		algebra::Matrix2f m1;
		m1.at(0,0) = 1;
		m1.at(1,1) = 1;
		CHECK_EQUAL(1,det(m1));
		CHECK(m1 == m1.transposed());

		float m4a[] ={1,2,3,4};
		float m5a[] ={1,3,2,4};
		algebra::Matrix2f m4(m4a), m5(m5a);
		CHECK_EQUAL(m4,m5.transposed());
	}
	
	TEST(Check_3x3_MatrixTransposition)
	{
		float m4a[] ={1,2,3,4,5,6,7,8,9};
		// |1 4 7|
		// |2 5 8|
		// |3 6 9|

		float m5a[] ={1,4,7,2,5,8,3,6,9};
		// |1 2 3|
		// |4 5 6|
		// |7 8 9|

		algebra::Matrix3f m4(m4a), m5(m5a);
		CHECK_EQUAL(m4.transposed(),m5);
	}

	TEST(Check_4x4_MatrixTransposition)
	{
		float m4a[] ={1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16};
		// |1 5 9  13|
		// |2 6 10 14|
		// |3 7 11 15|
		// |4 8 12 16|

		float m5a[] ={1,5,9,13,2,6,10,14,3,7,11,15,4,8,12,16};
		// | 1  2  3  4|
		// | 5  6  7  8|
		// | 9 10 11 12|
		// |13 14 15 16|

		algebra::Matrix4f m4(m4a), m5(m5a);
		CHECK_EQUAL(m4.transposed(),m5);
	}

	TEST(Check_4x4_MatrixMultiplication)
	{
		float m1a[] ={1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16};
		// |1 5 9  13|
		// |2 6 10 14|
		// |3 7 11 15|
		// |4 8 12 16|

		float m2a[] ={1,5,9,13,2,6,10,14,3,7,11,15,4,8,12,16};
		// | 1  2  3  4|
		// | 5  6  7  8|
		// | 9 10 11 12|
		// |13 14 15 16|

		float v3a[] = {17,18,19,20};

		float m1by2a[] = {
				276,304,332,360,	
				304,336,368,400,	
				332,368,404,440,	
				360,400,440,480};

		float v4a[] = {538,612,686,760};

		algebra::Matrix4f m1(m1a), m2(m2a), m1by2(m1by2a), m1by2res;

		algebra::Vector4f v3(v3a), v4(v4a),v4res;

		v4res = m1*v3;
		m1by2res = m1*m2;

		CHECK_EQUAL(m1by2,m1by2res);
		CHECK_EQUAL(v4,v4res);
	}

	TEST(Check_4x4_MatrixAddition)
	{
		float m1a[] ={1,2,3,4,  5,6,7,8,  9,10,11,12,  13,14,15,16};
		// |1 5 9  13|
		// |2 6 10 14|
		// |3 7 11 15|
		// |4 8 12 16|

		float m2a[] ={13,9,5,1,  14,10,6,2,  15,11,7,3,  16,12,8,4};
		// |13 14 15 16|
		// | 9 10 11 12|
		// | 5  6  7  8|
		// | 1  2  3  4|

		float mresa[] = {14,11,8,5,  19,16,13,10,  24,21,18,15,  29,26,23,20};
		
		algebra::Matrix4f m1(m1a), m2(m2a), mres(mresa);
		CHECK_EQUAL(mres, m1+m2);
		//std::cout<<m1+m2;
	}

	TEST(Check_2x2_Determinant)
	{
		float m1a[] ={12,1,35,2};
		algebra::Matrix2f m1(m1a);
		CHECK_EQUAL(-11, det(m1));
	}

	TEST(Check_3x3_Determinant)
	{
		float m1a[] ={12,1,35,2,31,45,5,9,1};
		algebra::Matrix3f m1(m1a);
		CHECK_EQUAL(-9060, det(m1));
	}

	TEST(Check_3x3_GetRowAndColumn)
	{
		float m1a[] = {12,1,35, 2,31,45, 5,9,1};
		float v1a[] = {12,2,5};
		float v2a[] = {1,31,9};
		float v3a[] = {35,45,1};

		float v4a[] = {12,1,35};
		float v5a[] = {2,31,45};
		float v6a[] = {5,9,1};

		algebra::Matrix3f m1(m1a);
		algebra::Row3f v1(v1a), v2(v2a), v3(v3a);
		algebra::Column3f v4(v4a), v5(v5a), v6(v6a);

		CHECK_EQUAL(v1, m1.getRow(0));
		CHECK_EQUAL(v2, m1.getRow(1));
		CHECK_EQUAL(v3, m1.getRow(2));

		CHECK_EQUAL(v4, m1.getCol(0));
		CHECK_EQUAL(v5, m1.getCol(1));
		CHECK_EQUAL(v6, m1.getCol(2));
	}

	TEST(Vector_Column_Transposition)
	{
		float m1a[] = {12,1,35, 2};
		algebra::Column4f c1(m1a);
		algebra::Row4f r1(m1a);
		CHECK_EQUAL(r1,c1.transposed());
		CHECK(!areOfSameDimension(r1,c1));
		CHECK(areOfSameDimension(r1,c1.transposed()));
	}

	TEST(MatrixAndScalarOperations)
	{
		float m1a[] = {12,1,35, 2,31,45, 5, 9,1};
		float m2a[] = {24,2,70, 4,62,90,10,18,2};
		algebra::Matrix3f m1(m1a), m2(m2a);
		CHECK_EQUAL(m2, m1*2);
		CHECK_EQUAL(m2, 2.0f*m1);
		CHECK_EQUAL(m1, m2/2);
	}

	TEST(MatrixPointersTests)
	{
		float m1a[] = {12,1,35, 2};
		algebra::Vector4f * v1p = new Vector4f(m1a);
		algebra::Vector4f v2(m1a);
		CHECK_EQUAL(v2,*v1p);
		delete v1p;
	}

	TEST(Matrix_2x2_Inverse)
	{
		float m1a[] = {7,3,-2,5};
		float mresa[] = {5.0/41, -3.0/41, 2.0/41, 7.0/41};
		algebra::Matrix2f m1(m1a), mres(mresa);

		CHECK_EQUAL(mres, inverse(m1));
	}

	TEST(TranslationMatrix)
	{
		algebra::Matrix4f m = geom::createTranslationMatrix(2,4,9.5);
		float vectorPos[4] = {1,2,3,1};
		float finalPos[4] = {3,6,12.5,1};
		algebra::Vector4f v(vectorPos);
		algebra::Vector4f vfinal(finalPos);
		algebra::Vector4f finalVector= m*v;

		CHECK_EQUAL(vfinal, finalVector);
	}

	TEST(RotationMatrix)
	{
		algebra::Matrix4f m = geom::createRotationMatrixAroundX(M_PI/4);
		float vectorPos[4] = {1,2,0,1};
		float finalPos[4] = {1,sqrt(2),sqrt(2),1};
		algebra::Vector4f v(vectorPos);
		algebra::Vector4f vfinal(finalPos);

		CHECK_EQUAL(vfinal, m*v);
	}
	TEST(ScalingMatrix)
	{
		algebra::Matrix4f m = geom::createScalingMatrix(2.5);
		float vectorPos1[4] = {1,2,0,1};
		float vectorPos2[4] = {1,2,0,0};
		float finalPos1[4] = {2.5,5,0,1};
		float finalPos2[4] = {2.5,5,0,0};
		algebra::Vector4f v1(vectorPos1);
		algebra::Vector4f v2(vectorPos2);
		algebra::Vector4f vfinal1(finalPos1);
		algebra::Vector4f vfinal2(finalPos2);

		CHECK_EQUAL(vfinal1, m*v1);
		CHECK_EQUAL(vfinal2, m*v2);
	}
}


